package org.datasetcreator.reader;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.RootClass;

@SuppressWarnings("unchecked")
public class EntityGenerator {

	private static final Log LOG = LogFactory.getLog(EntityGenerator.class);

	private static Map<Class, List> entitiesMap = new HashMap<Class, List>();

	private EntityGenerator() {
		super();
	}

	public static EntityGenerator getEntityGenerator() {
		return new EntityGenerator();
	}

	public Map<Class, List> getEntitiesMap() {
		return Collections.unmodifiableMap(entitiesMap);
	}

	public void generateMainEntities(Configuration conf, int numberOfEnt)
			throws ClassNotFoundException, IllegalArgumentException,
			IllegalAccessException, InstantiationException {
		if (LOG.isInfoEnabled()) {
			LOG.info("Generating main entities....");
		}
		Iterator classIterator = conf.getClassMappings();
		while (classIterator.hasNext()) {
			RootClass rootClass = (RootClass) classIterator.next();
			String className = rootClass.getClassName();
			Class loaded = Class.forName(className);
			generateEntities(loaded, numberOfEnt);
		}

	}

	public void generateCollectionEntities(Configuration conf, int numberOfEnt)
			throws ClassNotFoundException, IllegalArgumentException,
			IllegalAccessException, InstantiationException {
		if (LOG.isInfoEnabled()) {
			LOG.info("Generating entities for collection....");
		}
		Iterator collectionIterator = conf.getCollectionMappings();
		while (collectionIterator.hasNext()) {
			Collection hibernateColl = (Collection) collectionIterator.next();
			// The class of the collection
			Class collectionClass = hibernateColl.getElement().getType()
					.getReturnedClass();
			java.util.Collection<Object> generated = generateEntities(
					collectionClass, numberOfEnt);
			// the owner of the collection
			Class loaded = Class.forName(hibernateColl.getOwnerEntityName());
			System.err.println("----------------" + loaded);

		}

	}

	public List<Object> generateEntities(Class classToGenerate,
			int numberOfEntities) throws IllegalArgumentException,
			IllegalAccessException, InstantiationException {

		if (LOG.isDebugEnabled()) {
			LOG.debug("Generating entity for class : "
					+ classToGenerate.getSimpleName());
		}
		List currentList = new ArrayList();
		if (entitiesMap.containsKey(classToGenerate)) {
			return entitiesMap.get(classToGenerate);
		}

		List<Field> fields = new ArrayList<Field>();
		Class superClass = classToGenerate;
		while (!(superClass.getName().equals(Object.class.getName()))) {
			for (Field f : superClass.getDeclaredFields()) {
				fields.add(f);
			}
			superClass = superClass.getSuperclass();
		}

		long longValue = 1;
		int intValue = 1;
		double doubleValue = 1.0;
		float floatValue = 1.0F;
		short shortValue = 1;
		int stringCounter = 0;

		for (int i = 0; i < numberOfEntities; i++) {
			Object objectToSet = classToGenerate.newInstance();
			// here I will have all the field of the class that I have to set!
			for (Field field : fields) {
				if (!field.isAccessible()) {
					field.setAccessible(true);
				}
				Class type = field.getType();
				if (type.isArray()) {
					if (entitiesMap.containsKey(type)) {
						List listOFValues = entitiesMap.get(type);
						Object[] values = new Object[listOFValues.size()];
						int index = 0;
						for (Object o : listOFValues) {
							values[index++] = o;
						}
						field.set(objectToSet, values);
					} else {
						generateEntities(type, numberOfEntities);
					}
				} else if (type.isEnum()) {
					// TODO check if this is the proper way!!!
					String valueToSet = field.getClass().asSubclass(Enum.class)
							.newInstance().name();
					field.set(objectToSet, Enum.valueOf(type, valueToSet));

				} else if (String.class.equals(type)) {
					// do create unique values;
					String toSet = field.getName() + stringCounter++;
					field.set(objectToSet, toSet);
				} else if (boolean.class.equals(type)
						|| Boolean.class.equals(type)) {
					field.setBoolean(objectToSet, true);
				} else if (long.class.equals(type) || Long.class.equals(type)) {
					field.setLong(objectToSet, longValue);
					longValue++;
				} else if (int.class.equals(type) || Integer.class.equals(type)) {
					field.setInt(objectToSet, intValue);
					intValue++;
				} else if (char.class.equals(type)
						|| Character.class.equals(type)) {
					field.setChar(objectToSet, 'a');
				} else if (double.class.equals(type)
						|| Double.class.equals(type)) {
					field.setDouble(objectToSet, doubleValue);
					doubleValue++;
				} else if (float.class.equals(type) || Float.class.equals(type)) {
					field.setFloat(objectToSet, floatValue);
					floatValue++;
				} else if (short.class.equals(type) || Short.class.equals(type)) {
					field.setShort(objectToSet, shortValue);
					shortValue++;
				} else if (byte.class.equals(type) || Byte.class.equals(type)) {
					byte value = "a".getBytes()[0];
					field.setByte(objectToSet, value);
				}

			}

			currentList.add(objectToSet);
		}

		if (!currentList.isEmpty()) {
			entitiesMap.put(classToGenerate, currentList);
		}
		return currentList;

	}

}
